/* UniApo
 * File:   SCBFile.h
 * Author: Jeffrey Geyssens
 *
 * Created on April 26, 2010, 6:09 PM
 * This is the header file for the SCBFile object, which is loaded when a
 * SCB file is created by the user or loaded from file.
 *
 * This object is created and used by the SCBEditor.
 * Manually modifying the physical file using a OS based text editor
 * may provoke errors when reading to the object, you have been warned!
 *
 */

#ifndef _SCBFILE_H
#define	_SCBFILE_H

#include <iostream> //for input/output
#include <string> //for string operations
#include <fstream> //for files
#include "Section.h" //load section template
#include "SymbolDefinition.h"
#include "ProductionRuleContainer.h"
#include "UniApoFile.h"
using namespace std;

class SCBFile : public UniApoFile {
public:
    //constructor, destructor and copy constructor
    SCBFile();
    SCBFile(const SCBFile& orig);
    virtual ~SCBFile();

    //operations regarding SymbolDefinitions (subclass for next iteration)
    void addSymDef(string s, string d, string m, string t);
    void modifySymDef(int entryNo, string s, string d, string m, string t);
    void removeSymDef(int entryNo);
    void bulkSymDef(string path);
    void printSymDef();
    ProductionRuleContainer& getPR();
    Section<SymbolDefinition>& getSD();

    //operations regarding ProductionRules 
    void addPRule(string NT, string deriv);
    void modifyPRule(string NT);
    void removePRule(string NT, int entryNo);
    int count(string NT);
    void bulkPRule(string path);
    void printPRule();
    void printPRule(string NT);
    //Build file from object, store results at given path.
    void buildFile();
    //Build Specification file for parser
    void buildSpec(string filename);
    //Print file contents to screen.
    void print();
    //Load file from given path in to this object.
    void loadFile();

    //Give object a name
    void name(string n);

    //Some Extra functionality from MAPEditor
    void changeDRT(string from, string to);

private:

    Section<SymbolDefinition> SymDefSection;
    ProductionRuleContainer PRuleSection;

};

SCBFile::SCBFile() {
    this->SymDefSection.setHeader(".SYMDEF");
    this->SymDefSection.setFooter(".END_SYMDEF");
}

SCBFile::SCBFile(const SCBFile& orig) {
    SymDefSection = orig.SymDefSection;
    PRuleSection = orig.PRuleSection;
    filename = orig.filename;
    extension = orig.extension;


}

SCBFile::~SCBFile() {
}

void SCBFile::addSymDef(string s, string d, string m, string t) {
    //TODO: Force setting a symbol and a defintion(not here but in SCB editor)
    SymbolDefinition SymDefObject;
    SymDefObject.setSymbol(s);
    SymDefObject.setDefinition(d);

    if (t != "") {
        SymDefObject.setDRT(t);
    }

    SymDefObject.setMask(m);
    if ((t == "#") || (d == "#") || (s == "#") || (m == "#")) {
        cout << "procedure skipped" << endl;
    } else {
        SymDefSection.add(SymDefObject);
    }

}

void SCBFile::modifySymDef(int entryNo, string s, string d, string m, string t) {
    SymbolDefinition SymDefObject;
    //TODO: Why isn't this working???
    SymDefObject = SymDefSection.get(entryNo);
    if (s != "#") {
        SymDefObject.setSymbol(s);
    }
    if (d != "#") {

        SymDefObject.setDefinition(d);
    }
    if (m != "#") {
        SymDefObject.setMask(m);
    }

    if (t != "#") {
        //to-do fix check for real drt format (D:x)
        SymDefObject.setDRT(t);
    }

    SymDefSection.modify(SymDefObject, entryNo);
}

void SCBFile::removeSymDef(int entryNo) {
    this->SymDefSection.remove(entryNo);
}

void SCBFile::addPRule(string NT, string deriv) {
    //to do force syntax checking for NT and Derivation
    if ((NT == "#") || (deriv == "#")) {
        cout << "procedure skipped" << endl;
    } else {
        PRuleSection.addProduction(NT, deriv);
    }
}

void SCBFile::removePRule(string NT, int entryNo) {
    if ((NT == "#") || (entryNo == 0)) {
        cout << "procedure skipped" << endl;
    } else {
        PRuleSection.removeProduction(NT, entryNo);
    }
}

int SCBFile::count(string NT) {
    return PRuleSection.count(NT);
}

void SCBFile::printPRule(string NT) {
    PRuleSection.getProductions(NT);
}

void SCBFile::print() {
    this ->SymDefSection.print();
    this ->PRuleSection.print();
}

void SCBFile::printSymDef() {
    this ->SymDefSection.print();
}

void SCBFile::printPRule() {
    this ->PRuleSection.print();
}

void SCBFile::buildFile() {
    ofstream newSCBFile;
    newSCBFile.open(path.c_str());
    newSCBFile << "filename: " << filename.c_str() << "\n";
    newSCBFile << "extension: " << extension.c_str() << "\n \n";
    newSCBFile.close();
    this ->SymDefSection.buildFile(path);
    this ->PRuleSection.buildFile(path);
}

void SCBFile::buildSpec(string filename) {
    PRuleSection.buildSpec(filename);
}

//Load SCB file in to class structure

void SCBFile::loadFile() {
    ifstream SCBFile(path.c_str());
    string line;
    string s;
    string d;
    string m;
    string t;
    string nt;
    string deriv;

    if (SCBFile.is_open()) {
        getline(SCBFile, line);
        line = line.substr(9, line.size());
        if (line[0] == ' ') {
            line.erase(0, 1);
        }
        this->name(line);

        while (line != ".SYMDEF") {
            getline(SCBFile, line);
        }

        getline(SCBFile, line);

        //Symdefs are of type s(tab) d(tab) m(tab) DRT(tab)
        while (line != ".END_SYMDEF") {

            int i;
            int j;
            //Get Symbol
            j = line.find("\t");
            s = line.substr(0, j);
            //TODO: Can't we just substring? substr(j,size-j)
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            //Get Definition
            j = line.find("\t");
            d = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            //Get Mask
            j = line.find("\t");
            m = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            //Get DRT
            j = line.find("\n");
            t = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            //Put it all together
            this->addSymDef(s, d, m, t);
            //Get next line
            getline(SCBFile, line);
        }

        while (line != ".PRULE") {
            getline(SCBFile, line);
        }

        getline(SCBFile, line);

        while (line != ".END_PRULE") {

            int i;
            int j;
            j = line.find("\t");
            nt = line.substr(0, j);

            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find("\t");

            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find("\n");

            deriv = line.substr(0, j);
            this->addPRule(nt, deriv);
            getline(SCBFile, line);
        }
        SCBFile.close();
    }
}

void SCBFile::name(string n) {
    filename = n;
    extension = "SCB";
    this->setPath();
}

ProductionRuleContainer& SCBFile::getPR() {
    return this->PRuleSection;
}

Section<SymbolDefinition>& SCBFile::getSD() {
    return this->SymDefSection;
}

//Get Symbol Definitions in bulk
//Scans a file for rules of type s;d;m;(D:x)
//TODO: MARNIX Error skipping (if problem detected just go to newline)

void SCBFile::bulkSymDef(string path) {
    //Reads a csv file containing symbol definitions
    string line;
    string s;
    string d;
    string m;
    string drt;
    int i;
    int j;

    ifstream csvbulk(path.c_str());

    if (csvbulk.is_open()) {
        cout << "Processing File..." << endl;
    } else {
        cout << "No such file at specified location" << endl;
    }
    while (csvbulk.is_open()) {
        if (!csvbulk.eof()) {
            getline(csvbulk, line);

            j = line.find(";");
            s = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find(";");
            d = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find(";");
            m = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find("\n");
            drt = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            if (s != "") {
                this->addSymDef(s, d, m, drt);
            }
        } else {
            csvbulk.close();
        }
    }
}

//Get production rules in bulk
//Scans file for rules of type: <NT>;(derivaton)
//TODO: Error skipping

void SCBFile::bulkPRule(string path) {

    string line;
    string NT;
    string deriv;
    int i;
    int j;
    ifstream csvbulk(path.c_str());

    if (csvbulk.is_open()) {
        cout << "Processing File..." << endl;
    } else {
        cout << "No such file at specified location" << endl;
    }

    while (csvbulk.is_open()) {
        if (!csvbulk.eof()) {
            getline(csvbulk, line);

            j = line.find(";");
            NT = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find("\n");
            deriv = line.substr(0, j);

            if ((NT != "") && (deriv != "")) {
                this->addPRule(NT, deriv);
            }
        } else {
            csvbulk.close();
        }
    }
}

void SCBFile::changeDRT(string from, string to) {
    vector<SymbolDefinition>& SD = SymDefSection.getSectionData();
    vector<SymbolDefinition>::iterator it;
    //TODO: Pure iterators not working for some reason (byval byref)
    //      fix disgusting solution with i
    int i=1;
    for (it = SD.begin(); it != SD.end(); ++it) {

        if ((*it).getDRT() == from) {
            (*it).setDRT(to);
             this->modifySymDef(i, "#", "#","#", to);
            }
        i++;
    }
    this->buildFile();

}

#endif	/* _SCBFILE_H */
